import _ from 'lodash'
import { invokeInterface } from '../service/index'

export default class Observer {
  constructor(data) {
    this.cbs = []
    this.cache = []
    this.loading = false
    this.params = {}
    this.paramsGetters = []
    this.filters = []
    // 频繁调用update可能导致获取的不是最近调用的数据
    this.updateFlag = 0
    this.currentData = undefined
    this.type = 'interface' // or 'customGetter'
    Object.assign(this, data)
  }

  // 订阅
  subscribe(cb) {
    this.cbs.push(cb)
  }

  // 取消订阅
  unsubscribe(cb) {
    _.remove(this.cbs, one => one === cb)
  }

  // 通知订阅者
  notice(data) {
    this.cbs.forEach(one => {
      if (typeof one === 'function') {
        one(data)
      }
    })
  }

  // 添加请求参数设置器
  addParamsGetter(getter) {
    this.paramsGetters.push(getter)
  }

  // 删除请求参数设置器
  removeParamsGetter(getter) {
    _.remove(this.paramsGetters, getter)
  }

  // 根据所有请求参数设置器返回参数
  getParamsOfGetter() {
    let res = {}
    this.paramsGetters.forEach(one => {
      if (typeof one === 'function') {
        let paramsOne
        try {
          paramsOne = one()
        } catch (e) {

        }
        Object.assign(res, paramsOne || {})
      }
    })
    return res
  }

  // 添加结果过滤器
  addFilter(filter) {
    this.filters.push(filter)
  }

  // 删除结果过滤器
  removeFilter(filter) {
    _.remove(this.filters, filter)
  }

  // 执行所有过滤器(内置方法)
  doFilter(data) {
    this.filters.forEach(one => {
      if (typeof one === 'function') {
        try {
          data = one(data)
        } catch (e) {

        }
      }
    })
    return data
  }

  // 添加缓存(内置方法)
  addCache({ params, body, dataPromise }) {
    this.cache.push({ params, body, dataPromise })
    if (this.cache.length > 20) {
      this.cache.shift()
    }
  }

  // 获取数据
  getData(params, body) {
    return this.update(params, body, true)
  }

  // 获取当前数据
  getCurrentData() {
    return this.currentData
  }

  // 触发更新(noNotice 为true时不会触发notice 方法和currentData的更新)
  update(params, body, noNotice) {
    let currentFlag
    if (!noNotice) {
      currentFlag = ++this.updateFlag
    }

    if (!params) {
      params = {}
    }
    this.loading = true
    params = Object.assign(this.getParamsOfGetter(), params)
    if (this.isCache) {
      let find = this.cache.find(one => {
        return _.isEqual(one.params, params) && _.isEqual(one.body, body)
      })
      if (find) {
        find.dataPromise.then((data) => {
          data = this.doFilter(data)
          if (!noNotice) {
            this.notice(data)
            this.currentData = data
          }
        }).finally(() => {
          this.loading = false
        })
        return find.dataPromise
      }
    }
    let p
    if (this.type === 'interface') {
      p = invokeInterface(this.id, params, body)
    } else {
      p = Promise.resolve(this.customGetter(params, body))
    }

    p.then(data => {
      data = this.doFilter(data)
      if (currentFlag === this.updateFlag && !noNotice) {
        this.currentData = data
        this.notice(data)
      }
      return data
    }).finally(() => {
      this.loading = false
    })
    this.addCache({
      params: _.cloneDeep(params),
      body,
      dataPromise: p
    })
    return p
  }
}
